/***************************************************************************//**
*  \file       main.c
*
*  \details    test POWERLINK using RTX CMSIS V2 RTOS 
*
*  \author     blog.csdn.net/qq8864
*
*  \Tested with Proteus
*
* *****************************************************************************/ 
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include "RTE_Components.h"
#include  CMSIS_device_header
#include "cmsis_os2.h"
#include "stm32f4xx_hal.h"
 
#include "LED.h"
#include "Driver_USART.h"
#include "Driver_SPI.h"
#include "w5500_conf.h"

#include "app.h"
#include "event.h"
#include <oplk/oplk.h>
#include <oplk/debugstr.h>
#include <obdcreate/obdcreate.h>
#include <console/console.h>
#include <eventlog/eventlog.h>
#include <getopt/getopt.h>

//============================================================================//
//            G L O B A L   D E F I N I T I O N S                             //
//============================================================================//

//------------------------------------------------------------------------------
// const defines
//------------------------------------------------------------------------------
#define CYCLE_LEN           50000
#define NODEID              1                   // could be changed by command param
#define IP_ADDR             0xc0a86401          // 192.168.100.1
#define DEFAULT_GATEWAY     0xC0A864FE          // 192.168.100.C_ADR_RT1_DEF_NODE_ID
#define SUBNET_MASK         0xFFFFFF00          // 255.255.255.0

//------------------------------------------------------------------------------
// module global vars
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// global function prototypes
//------------------------------------------------------------------------------

//============================================================================//
//            P R I V A T E   D E F I N I T I O N S                           //
//============================================================================//

//------------------------------------------------------------------------------
// const defines
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// local types
//------------------------------------------------------------------------------
typedef struct
{
    UINT32          nodeId;
    tEventlogFormat logFormat;
    UINT32          logLevel;
    UINT32          logCategory;
    char            devName[128];
} tOptions;

//------------------------------------------------------------------------------
// local vars
//------------------------------------------------------------------------------
static const UINT8  aMacAddr_l[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static BOOL         fGsOff_l;

//------------------------------------------------------------------------------
// local function prototypes
//------------------------------------------------------------------------------
static int        getOptions(int argc_p,
                             char* const argv_p[],
                             tOptions* pOpts_p);
static tOplkError initPowerlink(UINT32 cycleLen_p,
                                const char* devName_p,
                                const UINT8* macAddr_p,
                                UINT32 nodeId_p);
static void       loopMain(void);
static void       shutdownPowerlink(void);

//============================================================================//
//            P U B L I C   F U N C T I O N S                                 //
//============================================================================//

//------------------------------------------------------------------------------

extern ARM_DRIVER_USART Driver_USART3;
extern ARM_DRIVER_SPI Driver_SPI3;
 
/* Variable definitions ------------------------------------------------------*/
static uint8_t rxBuffer[1024] = {0};
static uint8_t txBuffer[1024] = {0};
 
/* Function declarations -----------------------------------------------------*/
static void USART3_Callback(uint32_t event);
/**
  * @brief  USART1 callback function.
  * @param  event: USART events notification mask.
  * @return None.
  */
static void USART3_Callback(uint32_t event)
{
  if(event & ARM_USART_EVENT_RX_TIMEOUT)
  {
    Driver_USART3.Control(ARM_USART_ABORT_RECEIVE, 1);
    
    uint32_t length = Driver_USART3.GetRxCount();
    
    memcpy(txBuffer, rxBuffer, length);
    //printf("rx len:%d\n",length);
    //Driver_USART1.Send(txBuffer, length);
    Driver_USART3.Receive(rxBuffer, sizeof(rxBuffer));
  }
}

extern int stdout_init(void);
void uart_init()
{
	
	Driver_USART3.Initialize(USART3_Callback);
  Driver_USART3.PowerControl(ARM_POWER_FULL);
  Driver_USART3.Control(ARM_USART_MODE_ASYNCHRONOUS |
                        ARM_USART_DATA_BITS_8 |
                        ARM_USART_PARITY_NONE |
                        ARM_USART_STOP_BITS_1 |
                        ARM_USART_FLOW_CONTROL_NONE, 115200);
  Driver_USART3.Control(ARM_USART_CONTROL_TX, 1);
  Driver_USART3.Control(ARM_USART_CONTROL_RX, 1);
  
  Driver_USART3.Receive(rxBuffer, sizeof(rxBuffer));
}

void mySPI_callback(uint32_t event)
{
	
}
void spi_init()
{
	int ret = 0;
	/* Initialize the SPI driver */
  ret = Driver_SPI3.Initialize(mySPI_callback);
	if(ret!=0){
		printf("Initialize error,ret=%d\n",ret);
	}
    /* Power up the SPI peripheral */
  ret = Driver_SPI3.PowerControl(ARM_POWER_FULL);
	if(ret!=0){
		printf("PowerControl error,ret=%d\n",ret);
	}
    /* Configure the SPI to Master, 8-bit mode @10000 kBits/sec */
  ret = Driver_SPI3.Control(ARM_SPI_MODE_MASTER |ARM_SPI_SS_MASTER_UNUSED| ARM_SPI_CPOL1_CPHA1 | ARM_SPI_MSB_LSB  | ARM_SPI_SS_SLAVE_SW|ARM_SPI_DATA_BITS(8), 1000000);
	if(ret!=0){
		printf("Control_0 error,ret=%d\r\n",ret);
	}
  /* SS line = INACTIVE = HIGH */
  //ret = Driver_SPI3.Control(ARM_SPI_CONTROL_SS, ARM_SPI_SS_INACTIVE);
	//if(ret!=0){
		//printf("Control_1 error,ret=%d\r\n",ret);
	//}
}

uint8_t spi_send_byte(uint8_t byte)
{
	uint8_t  dataout[2];
	uint8_t  datain[2];
	dataout[0] = byte;
	int ret = Driver_SPI3.Transfer(dataout,datain,1);
	if(ret != 0){
		printf("spi Transfer error,ret=%d\r\n",ret);
	}
	return datain[0];
}
 
/*
** This thread will turns ON and turns OFF the PORT-D LEDs with 1second delay.
**
**  Arguments:
**      arg  -> Argument of this thread. osThreadNew()'s 2nd arg has to come here. 
**   
*/
__NO_RETURN static void Main_Loop_Thread( void *arg ) 
{
  (void)arg;                            //unused variable
  for (;;)                              //infinite for loop
  {
    //Turn ON the LED of Port-D
    LED_On(0);
    osDelay(1000);                      //1sec delay
    //Turn OFF the LED of Port-D
    LED_Off(0);
    //GPIOD->BSRR = 0xFFFF0000;
    osDelay(1000);                      //1sec delay
  }
}
 
/*
** This thread will turns ON and turns OFF the PORT-E LEDs with 3second delay.
**
**  Arguments:
**      arg  -> Argument of this thread. osThreadNew()'s 2nd arg has to come here. 
**   
*/
__NO_RETURN static void LED_Blink_PortE( void *arg ) 
{
  (void)arg;                            //unused variable
  //set Port E as output
  //GPIOE->MODER = 0x55555555;
  for (;;)                              //infinite for loop
  {
    //Turn ON the LED of Port-E
    //GPIOE->BSRR = 0x0000FFFF;
		Driver_USART3.Send("123456", strlen("123456"));
		printf("hello test\r\n");
		set_w5500_ip();
	  //LED_SetOut(0);
		LED_On(1);
    osDelay(3000);  
		LED_Off(1);
		osDelay(3000);  
		//3sec delay
    //Turn OFF the LED of Port-E
    //GPIOE->BSRR = 0xFFFF0000;
    //osDelay(3000);                      //3sec delay
	  //LED_SetOut(1);
  }
}
 
/*
** main function
**
**  Arguments:
**      none
**   
*/ 
int main (int argc, char* argv[]) 
{
  tOplkError  ret = kErrorOk;
	tOptions    opts;

	// System Initialization
  SystemCoreClockUpdate();
	
	if (getOptions(argc, argv, &opts) < 0)
        return 0;
	
  LED_Initialize();
	uart_init();
	//stdout_init();
	printf("hello test\r\n");
	LED_On(2);
	spi_init();
	
	reset_w5500();
	set_w5500_mac();
	set_w5500_ip();
	
	/*
	eventlog_init(opts.logFormat,
                  opts.logLevel,
                  opts.logCategory,
                  (tEventlogOutputCb)console_printlogadd);

	initEvents(&fGsOff_l);*/

	printf("----------------------------------------------------\n");
	printf("openPOWERLINK console CN DEMO application\n");
	printf("Using openPOWERLINK stack: %s\n", oplk_getVersionString());
	printf("----------------------------------------------------\n");

/*	
	eventlog_printMessage(kEventlogLevelInfo,
												kEventlogCategoryGeneric,
												"demo_cn_console: Stack version:%s Stack configuration:0x%08X",
												oplk_getVersionString(),
												oplk_getStackConfiguration());

	
	
	ret = initPowerlink(CYCLE_LEN,
											opts.devName,
											aMacAddr_l,
											opts.nodeId);
	if (ret != kErrorOk){
		  printf("initPowerlink error\n");
			goto Exit;
	}
		

	ret = initApp();
	if (ret != kErrorOk){
		printf("initApp error\n");
		goto Exit;
	}*/
			
 
  osKernelInitialize();                       // Initialize CMSIS-RTOS
  osThreadNew(Main_Loop_Thread, NULL, NULL);   // Create application main thread
  osThreadNew(LED_Blink_PortE, NULL, NULL);   // Create application test thread
  osKernelStart();                            // Start thread execution
  for (;;) 
  {
    //Dummy infinite for loop.
  }
Exit:	
	 printf("openPOWERLINK console Exit\n");
	 shutdownApp();
   shutdownPowerlink();
	 return 0;
}

//============================================================================//
//            P R I V A T E   F U N C T I O N S                               //
//============================================================================//
/// \name Private Functions
/// \{

//------------------------------------------------------------------------------
/**
\brief  Initialize the openPOWERLINK stack

The function initializes the openPOWERLINK stack.

\param[in]      cycleLen_p          Length of POWERLINK cycle.
\param[in]      devName_p           Device name string.
\param[in]      macAddr_p           MAC address to use for POWERLINK interface.
\param[in]      nodeId_p            POWERLINK node ID.

\return The function returns a tOplkError error code.
*/
//------------------------------------------------------------------------------
static tOplkError initPowerlink(UINT32 cycleLen_p,
                                const char* devName_p,
                                const UINT8* macAddr_p,
                                UINT32 nodeId_p)
{
    tOplkError          ret = kErrorOk;
    tOplkApiInitParam   initParam;
    static char         devName[128];

    printf("Initializing openPOWERLINK stack...\n");
    eventlog_printMessage(kEventlogLevelInfo,
                          kEventlogCategoryControl,
                          "Initializing openPOWERLINK stack");

    eventlog_printMessage(kEventlogLevelInfo,
                          kEventlogCategoryGeneric,
                          "Select the network interface");
    if (devName_p[0] == '\0')
    {
         return kErrorIllegalInstance;
    }
    else
        strncpy(devName, devName_p, 128);

    memset(&initParam, 0, sizeof(initParam));
    initParam.sizeOfInitParam = sizeof(initParam);

    // pass selected device name to Edrv
    initParam.hwParam.pDevName = devName;
    initParam.nodeId = nodeId_p;
    initParam.ipAddress = (0xFFFFFF00 & IP_ADDR) | initParam.nodeId;

    /* write 00:00:00:00:00:00 to MAC address, so that the driver uses the real hardware address */
    memcpy(initParam.aMacAddress, macAddr_p, sizeof(initParam.aMacAddress));

    initParam.fAsyncOnly              = FALSE;
    initParam.featureFlags            = UINT_MAX;
    initParam.cycleLen                = cycleLen_p;             // required for error detection
    initParam.isochrTxMaxPayload      = C_DLL_ISOCHR_MAX_PAYL;  // const
    initParam.isochrRxMaxPayload      = C_DLL_ISOCHR_MAX_PAYL;  // const
    initParam.presMaxLatency          = 50000;                  // const; only required for IdentRes
    initParam.preqActPayloadLimit     = 36;                     // required for initialization (+28 bytes)
    initParam.presActPayloadLimit     = 36;                     // required for initialization of Pres frame (+28 bytes)
    initParam.asndMaxLatency          = 150000;                 // const; only required for IdentRes
    initParam.multiplCylceCnt         = 0;                      // required for error detection
    initParam.asyncMtu                = 1500;                   // required to set up max frame size
    initParam.prescaler               = 2;                      // required for sync
    initParam.lossOfFrameTolerance    = 500000;
    initParam.asyncSlotTimeout        = 3000000;
    initParam.waitSocPreq             = 1000;
    initParam.deviceType              = UINT_MAX;               // NMT_DeviceType_U32
    initParam.vendorId                = UINT_MAX;               // NMT_IdentityObject_REC.VendorId_U32
    initParam.productCode             = UINT_MAX;               // NMT_IdentityObject_REC.ProductCode_U32
    initParam.revisionNumber          = UINT_MAX;               // NMT_IdentityObject_REC.RevisionNo_U32
    initParam.serialNumber            = UINT_MAX;               // NMT_IdentityObject_REC.SerialNo_U32
    initParam.applicationSwDate       = 0;
    initParam.applicationSwTime       = 0;
    initParam.subnetMask              = SUBNET_MASK;
    initParam.defaultGateway          = DEFAULT_GATEWAY;
    sprintf((char*)initParam.sHostname, "%02x-%08x", initParam.nodeId, initParam.vendorId);
    initParam.syncNodeId              = C_ADR_SYNC_ON_SOA;
    initParam.fSyncOnPrcNode          = FALSE;

    // set callback functions
    initParam.pfnCbEvent = processEvents;

#if defined(CONFIG_KERNELSTACK_DIRECTLINK)
    initParam.pfnCbSync = processSync;
#else
    initParam.pfnCbSync = NULL;
#endif

    // Initialize object dictionary
    ret = obdcreate_initObd(&initParam.obdInitParam);
    if (ret != kErrorOk)
    {
        fprintf(stderr,
                "obdcreate_initObd() failed with \"%s\" (0x%04x)\n",
                debugstr_getRetValStr(ret),
                ret);
        eventlog_printMessage(kEventlogLevelFatal,
                              kEventlogCategoryControl,
                              "obdcreate_initObd() failed with \"%s\" (0x%04x)\n",
                              debugstr_getRetValStr(ret),
                              ret);
        return ret;
    }

    // initialize POWERLINK stack
    ret = oplk_initialize();
    if (ret != kErrorOk)
    {
        fprintf(stderr,
                "oplk_initialize() failed with \"%s\" (0x%04x)\n",
                debugstr_getRetValStr(ret),
                ret);
        eventlog_printMessage(kEventlogLevelFatal,
                              kEventlogCategoryControl,
                              "oplk_initialize() failed with \"%s\" (0x%04x)\n",
                              debugstr_getRetValStr(ret),
                              ret);
        return ret;
    }

    ret = oplk_create(&initParam);
    if (ret != kErrorOk)
    {
        fprintf(stderr,
                "oplk_create() failed with \"%s\" (0x%04x)\n",
                debugstr_getRetValStr(ret),
                ret);
        eventlog_printMessage(kEventlogLevelFatal,
                              kEventlogCategoryControl,
                              "oplk_create() failed with \"%s\" (0x%04x)\n",
                              debugstr_getRetValStr(ret),
                              ret);
        return ret;
    }

    return kErrorOk;
}

//------------------------------------------------------------------------------
/**
\brief  Main loop of demo application

This function implements the main loop of the demo application.
- It creates the sync thread which is responsible for the synchronous data
  application.
- It sends a NMT command to start the stack
- It loops and reacts on commands from the command line.
*/
//------------------------------------------------------------------------------
static void loopMain(void)
{
    tOplkError  ret;
    char        cKey = 0;
    BOOL        fExit = FALSE;

#if !defined(CONFIG_KERNELSTACK_DIRECTLINK)

#if defined(CONFIG_USE_SYNCTHREAD)
    system_startSyncThread(processSync);
#endif

#endif

    // start processing
    ret = oplk_execNmtCommand(kNmtEventSwReset);
    if (ret != kErrorOk)
        return;

    printf("Start POWERLINK stack... ok\n");
    printf("Digital I/O interface with openPOWERLINK is ready!\n");
    printf("\n-------------------------------\n");
    printf("Press Esc to leave the program\n");
    printf("Press r to reset the node\n");
    printf("Press i to increase the digital input\n");
    printf("Press d to decrease the digital input\n");
    printf("Press p to print the digital outputs\n");
    printf("-------------------------------\n\n");

    setupInputs();

    // wait for key hit
    while (!fExit)
    {
        if (console_kbhit())
        {
            cKey = (char)console_getch();

            switch (cKey)
            {
                case 'r':
                    ret = oplk_execNmtCommand(kNmtEventSwReset);
                    if (ret != kErrorOk)
                        fExit = TRUE;
                    break;

                case 'i':
                    increaseInputs();
                    break;

                case 'd':
                    decreaseInputs();
                    break;

                case 'p':
                    printOutputs();
                    break;

                case 0x1B:
                    fExit = TRUE;
                    break;

                default:
                    break;
            }
        }

//        if (system_getTermSignalState() != FALSE)
//        {
//            fExit = TRUE;
//            printf("Received termination signal, exiting...\n");
//            eventlog_printMessage(kEventlogLevelInfo,
//                                  kEventlogCategoryControl,
//                                  "Received termination signal, exiting...");
//        }

        if (oplk_checkKernelStack() == FALSE)
        {
            fExit = TRUE;
            fprintf(stderr, "Kernel stack has gone! Exiting...\n");
            eventlog_printMessage(kEventlogLevelFatal,
                                  kEventlogCategoryControl,
                                  "Kernel stack has gone! Exiting...");
        }

#if (defined(CONFIG_USE_SYNCTHREAD) || \
     defined(CONFIG_KERNELSTACK_DIRECTLINK))
        system_msleep(100);
#else
        processSync();
#endif
    }

#if (TARGET_SYSTEM == _WIN32_)
    printf("Press Enter to quit!\n");
    console_getch();
#endif
}

//------------------------------------------------------------------------------
/**
\brief  Shutdown the demo application

The function shuts down the demo application.
*/
//------------------------------------------------------------------------------
static void shutdownPowerlink(void)
{
    UINT    i;

    fGsOff_l = FALSE;

#if (!defined(CONFIG_KERNELSTACK_DIRECTLINK) && \
     defined(CONFIG_USE_SYNCTHREAD))
    system_stopSyncThread();
    system_msleep(100);
#endif

    // halt the NMT state machine so the processing of POWERLINK frames stops
    oplk_execNmtCommand(kNmtEventSwitchOff);

    // small loop to implement timeout waiting for thread to terminate
    for (i = 0; i < 1000; i++)
    {
        if (fGsOff_l)
            break;
    }

    printf("Stack is in state off ... Shutdown\n");
    eventlog_printMessage(kEventlogLevelInfo,
                          kEventlogCategoryControl,
                          "Stack is in state off ... Shutdown openPOWERLINK");

    oplk_destroy();
    oplk_exit();
}

//------------------------------------------------------------------------------
/**
\brief  Get command line parameters

The function parses the supplied command line parameters and stores the
options at pOpts_p.

\param[in]      argc_p              Argument count.
\param[in]      argv_p              Pointer to arguments.
\param[out]     pOpts_p             Pointer to store options

\return The function returns the parsing status.
\retval 0           Successfully parsed
\retval -1          Parsing error
*/
//------------------------------------------------------------------------------
static int getOptions(int argc_p,
                      char* const argv_p[],
                      tOptions* pOpts_p)
{
    int opt;

    /* setup default parameters */
    strncpy(pOpts_p->devName, "\0", 128);
    pOpts_p->nodeId = NODEID;
    pOpts_p->logFormat = kEventlogFormatReadable;
    pOpts_p->logCategory = 0xffffffff;
    pOpts_p->logLevel = 0xffffffff;

    /* get command line parameters */
    while ((opt = getopt(argc_p, argv_p, "n:pv:t:d:")) != -1)
    {
        switch (opt)
        {
            case 'n':
                pOpts_p->nodeId = strtoul(optarg, NULL, 10);
                break;

            case 'd':
                strncpy(pOpts_p->devName, optarg, 128);
                break;

            case 'p':
                pOpts_p->logFormat = kEventlogFormatParsable;
                break;

           case 'v':
                pOpts_p->logLevel = strtoul(optarg, NULL, 16);
                break;

           case 't':
                pOpts_p->logCategory = strtoul(optarg, NULL, 16);
                break;

            default: /* '?' */
                printf("Usage: %s [-n NODE_ID] [-l LOGFILE] [-d DEV_NAME] [-v LOGLEVEL] [-t LOGCATEGORY] [-p]\n", argv_p[0]);
                printf(" -d DEV_NAME: Ethernet device name to use e.g. eth1\n");
                printf("              If option is skipped the program prompts for the interface.\n");
                printf(" -p: Use parsable log format\n");
                printf(" -v LOGLEVEL: A bit mask with log levels to be printed in the event logger\n");
                printf(" -t LOGCATEGORY: A bit mask with log categories to be printed in the event logger\n");

                return -1;
        }
    }
    return 0;
}

/// \}